﻿// --- Copyright (c) 2010 Steven Hartgers ---
// Copyright (C) 2010 Steven Hartgers All rights are reserved.
// Author contact: fiinix00/at\gmail.com
// ------------------------------------------------------------------------
// This program is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more detail
//  
// You should have received a copy of the GNU General Public License 
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Hack.Conversion;
using System.Collections.Specialized;
using System.Net.Mime;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Web;
using System.Runtime.InteropServices;
using Hack.SystemAPI;
using Microsoft.Win32;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using Hack.Attributes;
using NetFx.AGIL;
using NetFx.SMIL;
using System.Drawing.Imaging;
using SMath = System.Math;
using SConsole = System.Console;
using System.Globalization;
using Hack.DataStructures;
using System.Collections.ObjectModel;
using System.Data;
using Hack.Reflection;
using System.Linq.Expressions;
using Hack.Synchronization;
using Hack.Properties;

namespace Hack
{
    public class Gaussian
    {
        public float X1 { get; internal set; }
        public float X2 { get; internal set; }
        public Gaussian(float x1, float x2)
        {
            this.X1 = x1;
            this.X2 = x2;
        }
    }
}
namespace Hack.Extensions
{
    
    public static class ExtensionViewer
    {
        public static IEnumerable<Type> GetExtensionClasses()
        {
            return Hack.Helpers.ClassHelpers.GetAllClassesTYPE("Hack.Extensions")
                .Where(type => type.HasAttribute<Extension>());
        }
    }

    [Extension]
    public static class Extensions
    {

        public static Gaussian GaussianRandom(this Random rnd)
        {
            double x1, x2, w, y1, y2, h1, h2;

            do
            {
                h1 = (float)rnd.NextDouble();
                h2 = (float)rnd.NextDouble();
                x1 = 2.0 * h2 - 1.0;
                x2 = 2.0 * h2 - 1.0;
                w = x1 * x1 + x2 * x2;
            } while (w >= 1.0);

            w = SMath.Sqrt((-2.0 * SMath.Log(w)) / w);
            y1 = x1 * w;
            y2 = x2 * w;
            return new Gaussian((float)y1, (float)y2);
        }
        public static void FastForm(Action<Form> formSettings, Control[] controls)
        {
            Form f = new Form();
            formSettings(f);

            f.Controls.AddRange(controls);

            Application.Run(f);
        }
        public static void PreviewInForm(this Control c)
        {
            FastForm((form) => { form.Text = "Preview"; }, new Control[] { c });
        }
        public static void PreviewInForm(this System.Drawing.Image c)
        {
            PreviewInForm(new PictureBox() { Image = c });
        }
        public static void PreviewInForm(this Bitmap c)
        {
            PreviewInForm(new PictureBox() { Image = c });
        }

        public static int ToInt(this string s)
        {
            return int.Parse(s);
        }
        public static double ToDouble(this string s)
        {
            return double.Parse(s);
        }
        public static bool ToBool(this string s)
        {
            return bool.Parse(s);
        }

        public static void Out(this Object o)
        {
            System.Console.WriteLine(o);
        }
        public static void Out(this Object o, TextWriter outStream)
        {
            outStream.WriteLine(o);
        }
        public static void Out(this Object o, string startStr)
        {
            Out(o, startStr, string.Empty);
        }
        public static void Out(this Object o, string startStr, string endStr)
        {
            System.Console.WriteLine(String.Format("{0}{1}{2}", startStr, o, endStr));
        }

        public static void Alert(this Object o, string title)
        {
            AnytimeMessageBox.Say(o.ToString(), title);
        }

        public static bool Odd(this int value)
        {
            return value % 2 != 0;
        }

        public static bool Between(this int value, int min, int max)
        {
            return value >= min && value <= max;
        }

        public static string Ordinalize(this int value)
        {

            string ordinalized;

            if ((value % 100).Between(11, 13))
            {
                ordinalized = "th";
            }
            else
            {
                switch (value % 10)
                {
                    case 1:
                        ordinalized = "st"; break;
                    case 2:
                        ordinalized = "nd"; break;
                    case 3:
                        ordinalized = "rd"; break;
                    default:
                        ordinalized = "th"; break;
                }
            }

            return value + ordinalized;
        }

        public static bool IsValidEmailAddress(this string s)
        {
            Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
            return regex.IsMatch(s);
        }
        public static bool IsNullOrEmpty(this string s)
        {
            // Notice I trim it too :)
            return (s == null || s.Trim().Length == 0);
        }
        public static bool ContainsIn(this object o, IEnumerable c)
        {
            foreach (object i in c)
                if (i.Equals(o))
                    return true;

            return false;
        }
        public static void ContainsIn(this object o, IEnumerable c, Action<Object> ifTrue)
        {
            foreach (object i in c)
                if (i.Equals(o))
                {
                    ifTrue(i);
                }
        }
        public static object Find(this object o, IEnumerable c)
        {
            foreach (object i in c)
            {
                if (i.Equals(o))
                {
                    return i;
                }
            }
            return null;
        }

        /// <summary>
        /// Instable Casting, use with care
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="o"></param>
        /// <returns></returns>
        public static T As<T>(this object o) where T : struct
        {
            return (T)o;
        }

        public static IntPtr AddressOf(this object o)
        {
            IntPtr? p = null;
            GCHandle handle = GCHandle.Alloc(o, GCHandleType.Pinned);
            IntPtr pointer = GCHandle.ToIntPtr(handle);
            p = pointer;
            handle.Free();
            return (IntPtr)p;
        }

        public static IEnumerable<int> To(this int start, int end)
        {
            var diff = end - start > 0 ? 1 : -1;
            for (var current = start; current != end; current += diff)
                yield return current;
        }

        public static IEnumerable<char> To(this char start, char end)
        {
            var iStart = (int)start;
            var iEnd = (int)end;
            var diff = iEnd - iStart > 0 ? 1 : -1;
            for (var current = iStart; current != iEnd; current += diff)
                yield return (char)current;
        }


        public static IEnumerable<int> To(this int start, int endNotIncluded, int step)
        {
            for (int i = start; i < endNotIncluded; i += step)
                yield return i;
        }


        public static T? DefaultToNull<T>(this T value)
     where T : struct
        {
            return EqualityComparer<T>.Default.Equals(default(T), value) ? (T?)null : value;
        }

        public static T ThrowIfNullS<T>(this T? t, string mensaje)
         where T : struct
        {
            if (t == null)
                throw new NullReferenceException(mensaje);
            return t.Value;
        }

        public static T ThrowIfNullC<T>(this T t, string mensaje)
            where T : class
        {
            if (t == null)
                throw new NullReferenceException(mensaje);
            return t;
        }

        #region Map Try Do TryDo
        public static R Map<T, R>(this T t, Func<T, R> func)
        {
            return func(t);
        }

        public static R TryCC<T, R>(this T t, Func<T, R> func)
            where T : class
            where R : class
        {
            if (t == null) return null;
            return func(t);
        }

        public static R? TryCS<T, R>(this T t, Func<T, R> func)
            where T : class
            where R : struct
        {
            if (t == null) return null;
            return func(t);
        }

        public static R? TryCS<T, R>(this T t, Func<T, R?> func)
            where T : class
            where R : struct
        {
            if (t == null) return null;
            return func(t);
        }

        public static R TrySC<T, R>(this T? t, Func<T, R> func)
            where T : struct
            where R : class
        {
            if (t == null) return null;
            return func(t.Value);
        }

        public static R? TrySS<T, R>(this T? t, Func<T, R> func)
            where T : struct
            where R : struct
        {
            if (t == null) return null;
            return func(t.Value);
        }

        public static R? TrySS<T, R>(this T? t, Func<T, R?> func)
            where T : struct
            where R : struct
        {
            if (t == null) return null;
            return func(t.Value);
        }



        public static T Do<T>(this T t, Action<T> action)
        {
            action(t);
            return t;
        }

        public static T TryDoC<T>(this T t, Action<T> action) where T : class
        {
            if (t != null)
                action(t);
            return t;
        }

        public static T? TryDoS<T>(this T? t, Action<T> action) where T : struct
        {
            if (t != null)
                action(t.Value);
            return t;
        }
        #endregion
        public static IEnumerable<T> FollowC<T>(this T start, Func<T, T> next) where T : class
        {
            for (T i = start; i != null; i = next(i))
                yield return i;
        }

        public static IEnumerable<T> FollowS<T>(this T start, Func<T, T?> next) where T : struct
        {
            for (T? i = start; i.HasValue; i = next(i.Value))
                yield return i.Value;
        }

        public static IEnumerable<T> FollowS<T>(this T? start, Func<T, T?> next) where T : struct
        {
            for (T? i = start; i.HasValue; i = next(i.Value))
                yield return i.Value;
        }
        public static IEnumerable<int> DownTo(this int startNotIncluded, int end, int step)
        {
            for (int i = startNotIncluded - 1; i >= end; i -= step)
                yield return i;
        }

        public static IEnumerable<T> For<T>(this T start, Func<T, bool> condition, Func<T, T> incremento)
        {
            for (T i = start; condition(i); i = incremento(i))
                yield return i;
        }
    }
    [Extension]
    public static class IntExtensions
    {
        public static void SleepMs(this int i)
        {
            Thread.Sleep(i);
        }
        public static void SleepSec(this int i)
        {
            Thread.Sleep(new TimeSpan(0, 0, i));
        }
        public static void Upto(this int target, int topInclusive, Action<int> @do)
        {
            for (var i = target; i < topInclusive; i++)
                @do(i);
        }

        public static void Upto(this int target, int topInclusive, Action @do)
        {
            for (var i = target; i < topInclusive; i++)
                @do();
        }

        public static void Times(this int target, Action @do)
        {
            for (var i = 0; i < target; i++)
                @do();
        }

        public static void Times(this int target, Action<int> @do)
        {
            for (var i = 0; i < target; i++)
                @do(i);
        }
        public static bool IsEven(this int i)
        {
            return i % 1 == 0;
        }
        public static bool IsOdd(this int i)
        {
            return i % 1 == 1;
        }
        public static bool BiggerThan(this int i, int value)
        {
            return i > value;
        }
        public static bool SmalerThan(this int i, int value)
        {
            return i < value;
        }
    }
    [Extension]
    public static class FloatExtensions
    {
        public static float[][] Multiply(float[][] f1, float[][] f2)
        {
            float[][] X = new float[5][];
            for (int d = 0; d < 5; d++)
                X[d] = new float[5];
            int size = 5;
            float[] column = new float[5];
            for (int j = 0; j < 5; j++)
            {
                for (int k = 0; k < 5; k++)
                {
                    column[k] = f1[k][j];
                }
                for (int i = 0; i < 5; i++)
                {
                    float[] row = f2[i];
                    float s = 0;
                    for (int k = 0; k < size; k++)
                    {
                        s += row[k] * column[k];
                    }
                    X[i][j] = s;
                }
            }
            return X;
        }
    }
    [Extension]
    public static class BooleanExtensions
    {
        public static bool IsTrue(this bool b)
        {
            return b == true;
        }
        public static bool IsFalse(this bool b)
        {
            return b == false;
        }
        public static void IfTrue(this bool b, Action action)
        {
            if (b.IsTrue())
            {
                action();
            }
        }
        public static void IfFalse(this bool b, Action action)
        {
            if (b.IsFalse())
            {
                action();
            }
        }
        public static bool Is(this object o, object o2)
        {
            if (o.Equals(o2))
                return true;
            return false;
        }
        public static bool Is<T>(this T o, T o2)
        {
            if (o.Equals(o2))
                return true;
            return false;
        }
    }
    [Extension]
    public static class DateExtensions
    {
        public static TimeSpan Hours(this int value)
        {
            return TimeSpan.FromHours(value);
        }
        public static TimeSpan Minutes(this int value)
        {
            return TimeSpan.FromMinutes(value);
        }
        public static TimeSpan Seconds(this int value)
        {
            return TimeSpan.FromSeconds(value);
        }
        public static DateTime Ago(this TimeSpan timeSpan)
        {
            return DateTime.Now - timeSpan;
        }
        public static DateTime Until(this TimeSpan timeSpan, DateTime target)
        {
            return target - timeSpan;
        }
        public static bool Between(this DateTime dateToCheck, DateTime earlierDate, DateTime laterData)
        {
            return
                earlierDate <= dateToCheck &&
                dateToCheck < laterData;
        }
        public static int YearsTo(this DateTime min, DateTime max)
        {
            int result = max.Year - min.Year;
            if (max.Month < min.Month || (max.Month == min.Month & max.Day < min.Day))
                result--;

            return result;
        }

        public static int MonthsTo(this DateTime min, DateTime max)
        {
            int result = max.Month - min.Month + (max.Year - min.Year) * 12;
            if (max.Day < min.Day)
                result--;

            return result;
        }

        public static DateSpan DateSpanTo(this DateTime min, DateTime max)
        {
            return DateSpan.FromToDates(min, max);
        }

        public static DateTime Add(this DateTime date, DateSpan dateSpan)
        {
            return dateSpan.AddTo(date);
        }
        [OrginalAuthor(Author = "fullerjc", Url = "http://code.google.com/p/jonfuller/source/browse/trunk/code/CoreLib/src/CoreLib/Extensions/RangeExt.cs")]
        public static void ForEachDay(this Range<DateTime> target, Action<DateTime> action)
        {
            var split = new List<DateTime>();
            var current = target.Start;
            while (current <= target.End)
            {
                split.Add(current);
                current = current.AddDays(1);
            }
            split.ForEach(action);
        }
        public static DateTime GetFirstDateOfMonth(this DateTime date)
        {
            return date == DateTime.MinValue ? date : new DateTime(date.Year, date.Month, 1);
        }
        public static DateTime GetFirstDateOfWeek(this DateTime date)
        {
            if (date == DateTime.MinValue)
                return date;

            int week = date.GetWeekNumber();
            while (week == date.GetWeekNumber())
                date = date.AddDays(-1);
            return date.AddDays(1);
        }
        public static DateTime GetLastDateOfMonth(this DateTime date)
        {
            return date == DateTime.MaxValue ? date : new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
        }
        public static DateTime GetLastDateOfWeek(this DateTime date)
        {
            if (date == DateTime.MaxValue)
                return date;

            int week = date.GetWeekNumber();
            while (week == date.GetWeekNumber())
                date = date.AddDays(1);
            return date.AddDays(-1);
        }
        public static int GetWeekNumber(this DateTime date)
        {
            //Constants
            const int JAN = 1;
            const int DEC = 12;
            const int LASTDAYOFDEC = 31;
            const int FIRSTDAYOFJAN = 1;
            const int THURSDAY = 4;
            bool thursdayFlag = false;

            //Get the day number since the beginning of the year
            int dayOfYear = date.DayOfYear;

            //Get the first and last weekday of the year
            int startWeekDayOfYear = (int)(new DateTime(date.Year, JAN, FIRSTDAYOFJAN)).DayOfWeek;
            int endWeekDayOfYear = (int)(new DateTime(date.Year, DEC, LASTDAYOFDEC)).DayOfWeek;

            //Compensate for using monday as the first day of the week
            if (startWeekDayOfYear == 0)
                startWeekDayOfYear = 7;
            if (endWeekDayOfYear == 0)
                endWeekDayOfYear = 7;

            //Calculate the number of days in the first week
            int daysInFirstWeek = 8 - (startWeekDayOfYear);

            //Year starting and ending on a thursday will have 53 weeks
            if (startWeekDayOfYear == THURSDAY || endWeekDayOfYear == THURSDAY)
                thursdayFlag = true;

            //We begin by calculating the number of FULL weeks between
            //the year start and our date. The number is rounded up so
            //the smallest possible value is 0.
            int fullWeeks = (int)System.Math.Ceiling((dayOfYear - (daysInFirstWeek)) / 7.0);
            int resultWeekNumber = fullWeeks;

            //If the first week of the year has at least four days, the
            //actual week number for our date can be incremented by one.
            if (daysInFirstWeek >= THURSDAY)
                resultWeekNumber = resultWeekNumber + 1;

            //If the week number is larger than 52 (and the year doesn't
            //start or end on a thursday), the correct week number is 1.
            if (resultWeekNumber > 52 && !thursdayFlag)
                resultWeekNumber = 1;

            //If the week number is still 0, it means that we are trying
            //to evaluate the week number for a week that belongs to the
            //previous year (since it has 3 days or less in this year).
            //We therefore execute this function recursively, using the
            //last day of the previous year.
            if (resultWeekNumber == 0)
                resultWeekNumber = GetWeekNumber(new DateTime(date.Year - 1, DEC, LASTDAYOFDEC));
            return resultWeekNumber;
        }
        public static bool IsSameDate(this DateTime date, DateTime compareDate)
        {
            return date.Year == compareDate.Year &&
                   date.Month == compareDate.Month &&
                   date.Day == compareDate.Day;
        }
    }
    [Extension]
    public static class HTMLExtensions
    {
        public static string ToHtmlTable<T>(this IEnumerable<T> list, string tableSyle, string headerStyle, string rowStyle, string alternateRowStyle)
        {
            var result = new StringBuilder();
            if (String.IsNullOrEmpty(tableSyle))
            {
                result.Append("<table id=\"" + typeof(T).Name + "Table\">");
            }
            else
            {
                result.Append("<table id=\"" + typeof(T).Name + "Table\" class=\"" + tableSyle + "\">");
            }

            var propertyArray = typeof(T).GetProperties();

            foreach (var prop in propertyArray)
            {
                if (String.IsNullOrEmpty(headerStyle))
                {
                    result.AppendFormat("<th>{0}</th>", prop.Name);
                }
                else
                {
                    result.AppendFormat("<th class=\"{0}\">{1}</th>", headerStyle, prop.Name);
                }
            }


            for (int i = 0; i < list.Count(); i++)
            {
                if (!String.IsNullOrEmpty(rowStyle) && !String.IsNullOrEmpty(alternateRowStyle))
                {
                    result.AppendFormat("<tr class=\"{0}\">", i % 2 == 0 ? rowStyle : alternateRowStyle);
                }

                else
                {
                    result.AppendFormat("<tr>");
                }
                foreach (var prop in propertyArray)
                {
                    object value = prop.GetValue(list.ElementAt(i), null);
                    result.AppendFormat("<td>{0}</td>", value ?? String.Empty);
                }
                result.AppendLine("</tr>");
            }

            result.Append("</table>");

            return result.ToString();
        }
        public static string ToHtmlTable<T>(this IEnumerable<T> list)
        {
            return
                StringExtrnsions.Append(
                    @"<style type = ""text/css""> .tableStyle{border: solid 5 green;} 
                        th.header{ background-color:#FF3300} tr.rowStyle { background-color:#33FFFF; 
                        border: solid 1 black; } tr.alternate { background-color:#99FF66; 
                        border: solid 1 black;}</style>",
                HTMLExtensions
                        .ToHtmlTable(list, "tableStyle", "header", "rowStyle", "alternate"));
        }
        public static string ToQueryString(this NameValueCollection collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");

            var sb = new StringBuilder();

            var names = collection.AllKeys;
            for (var i = 0; i < names.Length; i++)
            {
                var name = names[i];
                var values = collection.GetValues(i);

                if (values == null)
                    continue;

                foreach (var value in values)
                {
                    sb.Append('&');

                    if (!string.IsNullOrEmpty(name))
                        sb.Append(name).Append('=');

                    sb.Append(string.IsNullOrEmpty(value)
                              ? string.Empty
                              : HttpUtility.UrlPathEncode(value));
                }
            }

            if (sb.Length == 0)
                return string.Empty;

            sb[0] = '?';
            return sb.ToString();
        }
    }

    [Extension]
    public static class ByteExtensions
    {
        /// <summary>
        /// Hex string lookup table.
        /// </summary>
        public static readonly string[] HexStringTable = new string[]
{
    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
    "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
    "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
    "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
    "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
    "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
    "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
};

        /// <summary>
        /// Returns a hex string representation of an array of bytes.
        /// </summary>
        /// <param name="value">The array of bytes.</param>
        /// <returns>A hex string representation of the array of bytes.</returns>
        public static string ToHex(this byte[] value)
        {
            StringBuilder stringBuilder = new StringBuilder();
            if (value != null)
            {
                foreach (byte b in value)
                {
                    stringBuilder.Append(HexStringTable[b]);
                }
            }

            return stringBuilder.ToString();
        }
        [Obsolete("Used for Void VS Void test, use ToHex, its 25x faster")]
        public static string ByteArrayToHexString(this byte[] Bytes)
        {
            StringBuilder Result = new StringBuilder();
            string HexAlphabet = "0123456789ABCDEF";

            foreach (byte B in Bytes)
            {
                Result.Append(HexAlphabet[(int)(B >> 4)]);
                Result.Append(HexAlphabet[(int)(B & 0xF)]);
            }

            return Result.ToString();
        }
        [Obsolete("Used for Void VS Void test, dont use")]
        public static byte[] HexStringToByteArray(this string Hex)
        {
            byte[] Bytes = new byte[Hex.Length / 2];
            int[] HexValue = new int[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D,
                                 0x0E, 0x0F };

            for (int x = 0, i = 0; i < Hex.Length; i += 2, x += 1)
            {
                Bytes[x] = (byte)(HexValue[Char.ToUpper(Hex[i + 0]) - '0'] << 4 |
                                  HexValue[Char.ToUpper(Hex[i + 1]) - '0']);
            }

            return Bytes;
        }
        public static void Inverse(this byte[] b)
        {
            for (int i = 0; i < b.Length; i++)
                b[i] = (byte)(b[i] ^ 255);//inverse 11110000 becomes 00001111
        }
        public static void Exverse(this byte[] b)
        {
            for (int i = 0; i < b.Length; i++)
                b[i] = (byte)(b[i] | 0);//exverse 00001111 becomes 11110000
        }
        public static byte[] InverseReturn(this byte[] b)
        {
            byte[] h = new byte[b.Length];
            for (int i = 0; i < b.Length; i++)
                h[i] = (byte)(b[i] ^ 255);//inverse 11110000 becomes 00001111
            return h;
        }
        public static byte[] ExverseReturn(this byte[] b)
        {
            var h = new byte[b.Length];
            for (int i = 0; i < b.Length; i++)
                h[i] = (byte)(b[i] | 0);//exverse 00001111 becomes 11110000
            return h;
        }
        public static byte[] AppendReturn(this byte[] thisBytes, byte[] data)
        {
            var c = new byte[thisBytes.Length + data.Length];

            Buffer.BlockCopy(thisBytes, 0, c, 0, thisBytes.Length);
            Buffer.BlockCopy(data, 0, c, thisBytes.Length, data.Length);
            return c;
        }
        public static void Append(this byte[] thisBytes, byte[] data)
        {
            var c = new byte[thisBytes.Length + data.Length];

            Buffer.BlockCopy(thisBytes, 0, c, 0, thisBytes.Length);
            Buffer.BlockCopy(data, 0, c, thisBytes.Length, data.Length);
            thisBytes = c;
        }
        public static byte[] Decompress(this byte[] gzBuffer)
        {
            var ms = new MemoryStream();
            int msgLength = BitConverter.ToInt32(gzBuffer, 0);
            ms.Write(gzBuffer, 4, gzBuffer.Length - 4);

            var buffer = new byte[msgLength];

            ms.Position = 0;
            var zip = new GZipStream(ms, CompressionMode.Decompress);
            zip.Read(buffer, 0, buffer.Length);

            return buffer;
        }
        public static byte[] Compress(this byte[] buffer)
        {
            var ms = new MemoryStream();
            var zip = new GZipStream(ms, CompressionMode.Compress, true);
            zip.Write(buffer, 0, buffer.Length);
            zip.Close();
            ms.Position = 0;

            var outStream = new MemoryStream();

            var compressed = new byte[ms.Length];
            ms.Read(compressed, 0, compressed.Length);

            var gzBuffer = new byte[compressed.Length + 4];
            Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
            Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
            return gzBuffer;
        }
        public static string ToBase64String(this byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        public static string ToBase64String(this byte[] b, Base64FormattingOptions options)
        {
            return Convert.ToBase64String(b, options);
        }
        public static string ToBase64String(this byte[] b, int offset, int length)
        {
            return Convert.ToBase64String(b, offset, length);
        }
        public static string ToBase64String(this byte[] b, int offset, int length, Base64FormattingOptions options)
        {
            return Convert.ToBase64String(b, offset, length, options);
        }
        public static bool SaveToFile(this byte[] b, string fileName)
        {
            try
            {
                FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);
                BinaryWriter bw = new BinaryWriter(fs);
                bw.Write(b);
                bw.Close();
                return true;
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.Message);
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="b">can be null</param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static byte[] LoadFromFile(this byte[] b, string fileName)
        {
            byte[] bytes = File.ReadAllBytes(fileName);
            b = bytes;
            return b;
        }
    }
    [Extension]
    public static class EnumExtension
    {
        public static string Name(this Enum e)
        {
            return Enum.GetName(e.GetType(), e);
        }
        public static string Description(this Enum e)
        {
            var attributes = (DescriptionAttribute[])
                e.GetType()
                    .GetField(e.ToString())
                        .GetCustomAttributes(typeof(DescriptionAttribute), false);

            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }
        public static T[] EnumToArray<T>(this Enum e)
        {
            Type enumType = typeof(T);
            if (enumType.BaseType != typeof(Enum))
            {
                throw new ArgumentException("T must be a System.Enum");
            }
            return (Enum.GetValues(enumType) as IEnumerable<T>).ToArray();
        }
    }
    [Extension]
    public static class IEnumerableExtensions
    {

        public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
      IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
        {
            if (first == null) throw new ArgumentNullException("first");
            if (second == null) throw new ArgumentNullException("second");
            if (resultSelector == null) throw new ArgumentNullException("resultSelector");

            using (var e1 = first.GetEnumerator())
            using (var e2 = second.GetEnumerator())
                while (e1.MoveNext() && e2.MoveNext())
                    yield return resultSelector(e1.Current, e2.Current);
        }
        public static bool Empty<T>(this IEnumerable<T> collection)
        {
            foreach (var item in collection)
                return false;

            return true;
        }

        public static IEnumerable<T> NotNull<T>(this IEnumerable<T> collection) where T : class
        {
            return collection.Where(a => a != null);
        }

        public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> collection) where T : struct
        {
            return collection.Where(a => a.HasValue).Select(a => a.Value);
        }

        public static IEnumerable<T> And<T>(this IEnumerable<T> collection, T newItem)
        {
            foreach (var item in collection)
                yield return item;
            yield return newItem;
        }

        public static IEnumerable<T> PreAnd<T>(this IEnumerable<T> collection, T newItem)
        {
            yield return newItem;
            foreach (var item in collection)
                yield return item;
        }

        public static int IndexOf<T>(this IEnumerable<T> collection, T item)
        {
            int i = 0;
            foreach (var val in collection)
            {
                if (EqualityComparer<T>.Default.Equals(item, val))
                    return i;
                i++;
            }
            return -1;
        }

        public static int IndexOf<T>(this IEnumerable<T> collection, Func<T, bool> condition)
        {

            int i = 0;
            foreach (var val in collection)
            {
                if (condition(val))
                    return i;
                i++;
            }
            return -1;
        }

        public static T Single<T>(this IEnumerable<T> collection, string errorMessage)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                    throw new InvalidOperationException(errorMessage);

                T current = enumerator.Current;

                if (!enumerator.MoveNext())
                    return current;
            }

            throw new InvalidOperationException(errorMessage);
        }

        public static T SingleOrDefault<T>(this IEnumerable<T> collection, string errorMessage)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                    return default(T); 

                T current = enumerator.Current;

                if (!enumerator.MoveNext())
                    return current;
            }

            throw new InvalidOperationException(errorMessage);
        }

        public static T First<T>(this IEnumerable<T> collection, string errorMessage)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                    throw new InvalidOperationException(errorMessage);

                return enumerator.Current;
            }
        }

        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (var item in collection)
                action(item);
            return collection;
        }

        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T, int> action)
        {
            int i = 0;
            foreach (var item in collection)
            {
                action(item, i);
                i++;
            }
            return collection;
        }

        public static string ToString<T>(this IEnumerable<T> collection, string separator)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var item in collection)
            {
                sb.Append(item.ToString());
                sb.Append(separator);
            }
            return sb.ToString(0, SMath.Max(0, sb.Length - separator.Length));  // Remove at the end is faster
        }

        public static string ToString<T>(this IEnumerable<T> collection, Func<T, string> toString, string separator)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var item in collection)
            {
                sb.Append(toString(item));
                sb.Append(separator);
            }
            return sb.ToString(0, SMath.Max(0, sb.Length - separator.Length));  // Remove at the end is faster
        }

        public static void ToConsole<T>(this IEnumerable<T> collection)
        {
            ToConsole(collection, a => a.ToString());
        }

        public static void ToConsole<T>(this IEnumerable<T> collection, Func<T, string> toString)
        {
            foreach (var item in collection)
                SConsole.WriteLine(toString(item));
        }

        public static void ToFile(this IEnumerable<string> collection, string fileName)
        {
            using (FileStream fs = File.Create(fileName))
            using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
            {
                foreach (var item in collection)
                    sw.WriteLine(item);
            }
        }

        public static void ToFile<T>(this IEnumerable<T> collection, Func<T, string> toString, string fileName)
        {
            collection.Select(toString).ToFile(fileName);
        }

        public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
        {
            DataTable table = new DataTable();

            List<MemberEntry<T>> members = MemberEntryFactory.GenerateList<T>();
            table.Columns.AddRange(members.Select(m => new DataColumn(m.Name, m.MemberInfo.ReturningType())).ToArray());
            collection.ForEach(
                e => table.Rows.Add(members.Select(m => m.Getter(e)).ToArray()));
            return table;
        }

        #region String Tables
        public static string[,] ToStringTable<T>(this IEnumerable<T> collection)
        {
            List<MemberEntry<T>> members = MemberEntryFactory.GenerateList<T>();

            string[,] result = new string[members.Count, collection.Count() + 1];

            for (int i = 0; i < members.Count; i++)
                result[i, 0] = members[i].Name;

            int j = 1;
            foreach (var item in collection)
            {
                for (int i = 0; i < members.Count; i++)
                    result[i, j] = members[i].Getter(item).TryCC(a => a.ToString()) ?? "";
                j++;
            }

            return result;
        }

        public static string FormatTable(this string[,] table)
        {
            return FormatTable(table, true);
        }

        public static string FormatTable(this string[,] table, bool longHeaders)
        {
            int width = table.GetLength(0);
            int height = table.GetLength(1);
            int start = height == 1 ? 0 : (longHeaders ? 0 : 1);

            int[] lengths = 0.To(width).Select(i => SMath.Max(3, start.To(height).Max(j => table[i, j].Length))).ToArray();

            return 0.To(height).Select(j => 0.To(width).ToString(i => table[i, j].PadChopRight(lengths[i]), " ")).ToString("\r\n");
        }

        public static void WriteFormatedStringTable<T>(this IEnumerable<T> collection, TextWriter textWriter, string title)
        {
            textWriter.WriteLine();
            textWriter.WriteLine(title ?? "Tabla");
            textWriter.WriteLine(collection.ToStringTable().FormatTable(false));
            textWriter.WriteLine();
        }

        public static void ToConsoleTable<T>(this IEnumerable<T> collection, string title)
        {
            collection.WriteFormatedStringTable(SConsole.Out, title);
        }

        public static string ToWikiTable<T>(this IEnumerable<T> collection)
        {
            string[,] table = collection.ToStringTable();

            string str = "{| class=\"data\"\r\n" + 0.To(table.GetLength(1))
                .Select(i => (i == 0 ? "! " : "| ") + table.Row(i).ToString(o => o == null ? "" : o.ToString(), i == 0 ? " !! " : " || "))
                .ToString("\r\n|-\r\n") + "\r\n|}";

            return str;
        }
        #endregion

        #region Min Max
        public static T WithMin<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
          where V : IComparable<V>
        {
            T result = default(T);
            bool hasMin = false;
            V min = default(V);
            foreach (var item in collection)
            {
                V val = valueSelector(item);
                if (!hasMin || val.CompareTo(min) < 0)
                {
                    hasMin = true;
                    min = val;
                    result = item;
                }
            }

            return result;
        }

        public static T WithMax<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
               where V : IComparable<V>
        {
            T result = default(T);
            bool hasMax = false;
            V max = default(V);

            foreach (var item in collection)
            {
                V val = valueSelector(item);
                if (!hasMax || val.CompareTo(max) > 0)
                {
                    hasMax = true;
                    max = val;
                    result = item;
                }
            }
            return result;
        }

        public static MinMax<T> WithMinMaxPair<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
        where V : IComparable<V>
        {
            T withMin = default(T), withMax = default(T);
            bool hasMin = false, hasMax = false;
            V min = default(V), max = default(V);
            foreach (var item in collection)
            {
                V val = valueSelector(item);
                if (!hasMax || val.CompareTo(max) > 0)
                {
                    hasMax = true;
                    max = val;
                    withMax = item;
                }

                if (!hasMin || val.CompareTo(min) < 0)
                {
                    hasMin = true;
                    min = val;
                    withMin = item;
                }
            }

            return new MinMax<T>(withMin, withMax);
        }

        public static MinMax<T> MinMaxPair<T>(this IEnumerable<T> collection)
    where T : IComparable<T>
        {
            bool has = false;
            T min = default(T), max = default(T);
            foreach (var item in collection)
            {
                if (!has)
                {
                    has = true;
                    min = max = item;
                }
                else
                {
                    if (item.CompareTo(max) > 0)
                        max = item;
                    if (item.CompareTo(min) < 0)
                        min = item;
                }
            }

            return new MinMax<T>(min, max);
        }

        public static MinMax<V> MinMaxPair<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
            where V : IComparable<V>
        {
            bool has = false;
            V min = default(V), max = default(V);
            foreach (var item in collection)
            {
                V val = valueSelector(item);

                if (!has)
                {
                    has = true;
                    min = max = val;
                }
                else
                {
                    if (val.CompareTo(max) > 0)
                        max = val;
                    if (val.CompareTo(min) < 0)
                        min = val;
                }
            }

            return new MinMax<V>(min, max);
        }

   
        #endregion

        #region Operation
        public static IEnumerable<S> BiSelect<T, S>(this IEnumerable<T> collection, Func<T, T, S> func)
        {
            return BiSelect(collection, func, BiSelectOptions.None);
        }

        public static IEnumerable<S> BiSelect<T, S>(this IEnumerable<T> collection, Func<T, T, S> func, BiSelectOptions options)
        {
            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                    yield break;

                
                T firstItem = enumerator.Current;
                if (options == BiSelectOptions.Initial || options == BiSelectOptions.InitialAndFinal)
                    yield return func(default(T), firstItem);

                T lastItem = firstItem;
                while (enumerator.MoveNext())
                {
                    T item = enumerator.Current;
                    yield return func(lastItem, item);
                    lastItem = item; 
                }

                if (options == BiSelectOptions.Final || options == BiSelectOptions.InitialAndFinal)
                    yield return func(lastItem, default(T));

                if (options == BiSelectOptions.Circular)
                    yield return func(lastItem, firstItem); 
            }
        }

        //return one element more
        public static IEnumerable<S> SelectAggregate<T, S>(this IEnumerable<T> collection, S seed, Func<S, T, S> aggregate)
        {
            yield return seed;
            foreach (var item in collection)
            {
                seed = aggregate(seed, item);
                yield return seed;
            }
        }

        public static List<Grouping<T,T>> GroupWhen<T>(this IEnumerable<T> collection, Func<T, bool> isGroupKey)
        {
            List<Grouping<T, T>> result = new List<Grouping<T, T>>();
            Grouping<T, T> group = null;
            foreach (var item in collection)
            {
                if (isGroupKey(item))
                {
                    group = new Grouping<T, T>(item); 
                    result.Add(group);
                }
                else
                {
                    if (group != null)
                        group.Add(item);
                }
            }

            return result;
        }

        public static IEnumerable<List<T>> GroupsOf<T>(this IEnumerable<T> collection, int groupSize)
        {
            List<T> newList = new List<T>(groupSize);
            foreach (var item in collection)
            {
                newList.Add(item);
                if (newList.Count == groupSize)
                {
                    yield return newList;
                    newList = new List<T>();
                }
            }

            if (newList.Count != 0)
                yield return newList;
        }

        public static IEnumerable<T> Slice<T>(this IEnumerable<T> collection, int firstIncluded, int toNotIncluded)
        {
            return collection.Skip(firstIncluded).Take(toNotIncluded - firstIncluded);
        }

        public static IOrderedEnumerable<T> Order<T>(this IEnumerable<T> collection) where T : IComparable<T>
        {
            return collection.OrderBy(a => a);
        }

        public static IOrderedQueryable<T> Order<T>(this IQueryable<T> collection) where T : IComparable<T>
        {
            return collection.OrderBy(a => a);
        }
        #endregion

        #region Zip
        public static IEnumerable<Hack.DataStructures.Tuple<A, B>> Zip<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB)
        {
            using (var enumA = colA.GetEnumerator())
            using (var enumB = colB.GetEnumerator())
            {
                while (enumA.MoveNext() && enumB.MoveNext())
                {
                    yield return new Hack.DataStructures.Tuple<A, B>(enumA.Current, enumB.Current);
                }
            }
        }

        public static void ZipForeach<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB, Action<A, B> actions)
        {
            using (var enumA = colA.GetEnumerator())
            using (var enumB = colB.GetEnumerator())
            {
                while (enumA.MoveNext() && enumB.MoveNext())
                {
                    actions(enumA.Current, enumB.Current);
                }
            }
        }

        public static IEnumerable<Hack.DataStructures.Tuple<A, B>> ZipStrict<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB)
        {
            using (var enumA = colA.GetEnumerator())
            using (var enumB = colB.GetEnumerator())
            {
                while (AssertoTwo(enumA.MoveNext(), enumB.MoveNext()))
                {
                    yield return new Hack.DataStructures.Tuple<A, B>(enumA.Current, enumB.Current);
                }
            }
        }

        public static IEnumerable<R> ZipStrict<A, B, R>(this IEnumerable<A> colA, IEnumerable<B> colB, Func<A, B, R> mixer)
        {
             using (var enumA = colA.GetEnumerator())
             using (var enumB = colB.GetEnumerator())
             {
                 while (AssertoTwo(enumA.MoveNext(), enumB.MoveNext()))
                 {
                     yield return mixer(enumA.Current, enumB.Current);
                 }
             }
        }

        public static void ZipForeachStrict<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB, Action<A, B> action)
        {
            using (var enumA = colA.GetEnumerator())
            using (var enumB = colB.GetEnumerator())
            {
                while (AssertoTwo(enumA.MoveNext(), enumB.MoveNext()))
                {
                    action(enumA.Current, enumB.Current);
                }
            }
        }

        static bool AssertoTwo(bool nextA, bool nextB)
        {
            if (nextA != nextB)
                if (nextA)
                    throw new InvalidOperationException(Resources.SecondCollectionsIsShorter);
                else
                    throw new InvalidOperationException(Resources.FirstCollectionIsShorter);
            else
                return nextA;
        }
        #endregion

        #region Conversions
       

        public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
        {
            return new HashSet<T>(source);
        }

        public static ReadOnlyCollection<T> ToReadOnly<T>(this IEnumerable<T> collection)
        {
            return collection == null ? null :
                collection as ReadOnlyCollection<T> ?? (collection as List<T> ?? collection.ToList()).AsReadOnly();
        }

        public static IEnumerable<T> AsThreadSafe<T>(this IEnumerable<T> source)
        {
            return new TreadSafeEnumerator<T>(source);
        }

        public static IEnumerable<T> ToProgressEnumerator<T>(this IEnumerable<T> source, out IProgressInfo pi)
        {
            pi = new Progress<T>(source, source.Count());
            return (IEnumerable<T>)pi;
        }

       

        public static void PushRange<T>(this Stack<T> stack, IEnumerable<T> elements)
        {
            foreach (var item in elements)
                stack.Push(item);
        }

        public static void EnqueueRange<T>(this Queue<T> queue, IEnumerable<T> elements)
        {
            foreach (var item in elements)
                queue.Enqueue(item);
        }

        public static void AddRange<T>(this HashSet<T> hashset, IEnumerable<T> coleccion)
        {
            foreach (var item in coleccion)
            {
                hashset.Add(item);
            }
        }
        #endregion

        public static void Run<TClassType>(this TClassType type, Action<TClassType> action)
        {
            action(type);
        }
        public static void Run<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
        {
            foreach (var item in source)
                action(item);
        }
        public static void Run<TSource>(this IEnumerable<TSource> source, Action<TSource> action, Action onDone)
        {
            Run(source, action);
            onDone();
        }
        public static void Run<TSource>(this TSource[,] source, Action<TSource> action)
        {
            source.Run(action);
        }

        public static IEnumerable<T> Do<T>(this IEnumerable<T> source, Action<T> action)
        {
            foreach (var item in source)
            {
                action(item);
                yield return item;
            }
        }
        public static IEnumerable<TOut> Map<TIn, TOut>(this IEnumerable<TIn> target, Func<TIn, TOut> mapper)
        {
            foreach (var item in target)
                yield return mapper(item);
        }
        public static IEnumerable<T> Collect<T>(this IEnumerable<T> target, Func<T, bool> predicate)
        {
            foreach (var item in target)
                if (predicate(item))
                    yield return item;
        }
        public static IEnumerable<T> Filter<T>(this IEnumerable<T> target, Func<T, bool> predicate)
        {
            return Collect(target, predicate);
        }
        public static bool IsEmpty<T>(this IEnumerable<T> target)
        {
            return target.Count() < 1;
        }
        public static IEnumerable<T> RemoveNulls<T>(this IEnumerable<T> target) where T : class
        {
            return Collect(target, t => t != null);
        }
        public static void ForEach<T>(this IEnumerable target, Action<T> action)
        {
            foreach (var item in target)
                action((T)item);
        }
        public static void ForEach<T>(this IEnumerable target, Action<T,int> action)
        {
            int i = 0;
            foreach (var item in target)
            {
                action((T)item, i++);
            }
        }

        public static string Join(this IEnumerable<string> target, string separator)
        {
            return string.Join(separator, target.ToArray());
        }
        //public static IEnumerable<TR> SelectMany<T, TR>(this IEnumerable<T> source, Func<T, IEnumerable<TR>> f)
        //{
        //    foreach (var item in source)
        //        foreach (var result in f(item))
        //            yield return result;
        //}
        public static void Execute<T>(this IEnumerable<T> sequence)
        {
#pragma warning disable 642
            foreach (var item in sequence) ;
#pragma warning restore 642
        }
        public static List<string> RemoveDuplicates(List<string> inputList)
        {
            var uniqueStore = new Dictionary<string, int>();
            var finalList = new List<string>();

            foreach (string currValue in inputList)
            {
                if (!uniqueStore.ContainsKey(currValue))
                {
                    uniqueStore.Add(currValue, 0);
                    finalList.Add(currValue);
                }
            }
            return finalList;
        }
        public static string Quote(this string obj)
        {
            return "\"" + obj.ToString() + "\"";
        }

        public static string ToJSonArray<T>(this IEnumerable<T> collection, Func<T, string> toString)
        {
            return "[" + collection.ToString(toString, ", ") + "]";
        }

        public static string ToJSonObject<K, V>(this IDictionary<K, V> dictionary, Func<K, string> keyQuoted, Func<V, string> value)
        {
            return "{" + dictionary.ToString(kvp =>
                "{0} : {1}".Formato(
                   keyQuoted(kvp.Key),
                   value(kvp.Value)), ", ") + "}";
        }

        public static string ToJSonArrayBig<T>(this IEnumerable<T> collection, Func<T, string> toString)
        {
            return "[\r\n" + collection.ToString(toString, ",\r\n").Indent(2) + "\r\n]";
        }

        public static string ToJSonObjectBig<K, V>(this IDictionary<K, V> dictionary, Func<K, string> keyQuoted, Func<V, string> value)
        {
            return "{\r\n" + dictionary.ToString(kvp =>
                "{0} : {1}".Formato(
                   keyQuoted(kvp.Key),
                   value(kvp.Value)), ",\r\n").Indent(3) + "\r\n}";
        }   
    }
    [Extension]
    public static class AssignmentExtensions
    {
        public static TR Let<T, TR>(this T t, Func<T, TR> f)
        {
            return f(t);
        }
    }
    [Extension]
    public static class AttributeExtensions
    {
        // Attribute.IsDefined shortcuts
        public static bool HasAttribute<T>(this object element) where T : Attribute
        { return Attribute.IsDefined(element.GetType(), typeof(T)); }
        public static bool HasAttribute<T>(this object element, bool inherit) where T : Attribute
        { return Attribute.IsDefined(element.GetType(), typeof(T), inherit); }
        public static bool HasAttribute<T>(this Assembly element) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T)); }
        public static bool HasAttribute<T>(this Assembly element, bool inherit) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T), inherit); }
        public static bool HasAttribute<T>(this MemberInfo element) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T)); }
        public static bool HasAttribute<T>(this MemberInfo element, bool inherit) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T), inherit); }
        public static bool HasAttribute<T>(this Module element) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T)); }
        public static bool HasAttribute<T>(this Module element, bool inherit) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T), inherit); }
        public static bool HasAttribute<T>(this ParameterInfo element) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T)); }
        public static bool HasAttribute<T>(this ParameterInfo element, bool inherit) where T : Attribute
        { return Attribute.IsDefined(element, typeof(T), inherit); }

        // Attribute.GetCustomAttribute shotcuts
        public static T GetAttribute<T>(this object element) where T : Attribute
        { return Attribute.GetCustomAttribute(element.GetType(), typeof(T)) as T; }
        public static T GetAttribute<T>(this object element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttribute(element.GetType(), typeof(T), inherit) as T; }
        public static T GetAttribute<T>(this Assembly element) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T)) as T; }
        public static T GetAttribute<T>(this Assembly element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T), inherit) as T; }
        public static T GetAttribute<T>(this MemberInfo element) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T)) as T; }
        public static T GetAttribute<T>(this MemberInfo element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T), inherit) as T; }
        public static T GetAttribute<T>(this Module element) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T)) as T; }
        public static T GetAttribute<T>(this Module element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T), inherit) as T; }
        public static T GetAttribute<T>(this ParameterInfo element) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T)) as T; }
        public static T GetAttribute<T>(this ParameterInfo element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttribute(element, typeof(T), inherit) as T; }

        // Attribute.GetCustomAttributes shortcuts
        public static T[] GetAttributes<T>(this object element) where T : Attribute
        { return Attribute.GetCustomAttributes(element.GetType(), typeof(T)) as T[]; }
        public static T[] GetAttributes<T>(this object element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttributes(element.GetType(), typeof(T), inherit) as T[]; }
        public static T[] GetAttributes<T>(this Assembly element) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T)) as T[]; }
        public static T[] GetAttributes<T>(this Assembly element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T), inherit) as T[]; }
        public static T[] GetAttributes<T>(this MemberInfo element) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T)) as T[]; }
        public static T[] GetAttributes<T>(this MemberInfo element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T), inherit) as T[]; }
        public static T[] GetAttributes<T>(this Module element) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T)) as T[]; }
        public static T[] GetAttributes<T>(this Module element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T), inherit) as T[]; }
        public static T[] GetAttributes<T>(this ParameterInfo element) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T)) as T[]; }
        public static T[] GetAttributes<T>(this ParameterInfo element, bool inherit) where T : Attribute
        { return Attribute.GetCustomAttributes(element, typeof(T), inherit) as T[]; }
    }

    [Extension]
    public static class ObjectExtensions
    {
        public static byte[] Serialize(this Object o)
        {
            var ms = new MemoryStream();
            var bf = new BinaryFormatter();
            bf.Serialize(ms, o);
            return ms.ToArray();
        }
        public static object DeSerialize(this byte[] b)
        {
            var ms = new MemoryStream(b);
            var formatter = new BinaryFormatter();
            return formatter.Deserialize(ms);
        }
    }
    [Extension]
    public static class UriExtensions
    {
        public static bool IsHttpOrHttps(this Uri uri)
        {
            if (uri == null) throw new ArgumentNullException("uri");
            return uri.Scheme == Uri.UriSchemeHttp
                || uri.Scheme == Uri.UriSchemeHttps;
        }
    }
    [Extension]
    public static class ContentExtensions
    {
        /// <summary>
        /// Determines whether content type is plain text.
        /// </summary>

        public static bool IsPlainText(this ContentType contentType)
        {
            if (contentType == null) throw new ArgumentNullException("contentType");
            return EqualsOrdinalIgnoreCase(MediaTypeNames.Text.Plain, contentType.MediaType);
        }

        /// <summary>
        /// Determines whether content media type is text.
        /// </summary>

        public static bool IsText(this ContentType contentType)
        {
            if (contentType == null) throw new ArgumentNullException("contentType");
            return EqualsOrdinalIgnoreCase("text", GetMediaBaseType(contentType));
        }

        /// <summary>
        /// Determines whether content type identifies an HTML document.
        /// </summary>

        public static bool IsHtml(this ContentType contentType)
        {
            if (contentType == null) throw new ArgumentNullException("contentType");
            return EqualsOrdinalIgnoreCase(MediaTypeNames.Text.Html, contentType.MediaType);
        }

        /// <summary>
        /// Determines whether content media type identifies an image.
        /// </summary>

        public static bool IsImage(this ContentType contentType)
        {
            if (contentType == null) throw new ArgumentNullException("contentType");
            return EqualsOrdinalIgnoreCase("image", GetMediaBaseType(contentType));
        }

        /// <summary>
        /// Gets the base media of the content type, e.g. text from text/plain.
        /// </summary>

        public static string GetMediaBaseType(this ContentType contentType)
        {
            if (contentType == null) throw new ArgumentNullException("contentType");
            var mediaType = contentType.MediaType;
            return mediaType.Substring(0, mediaType.IndexOf('/'));
        }

        /// <summary>
        /// Gets the media sub-type of the content type, e.g. plain from text/plain.
        /// </summary>

        public static string GetMediaSubType(this ContentType contentType)
        {
            if (contentType == null) throw new ArgumentNullException("contentType");
            var mediaType = contentType.MediaType;
            return mediaType.Substring(mediaType.IndexOf('/') + 1);
        }

        private static bool EqualsOrdinalIgnoreCase(string left, string right)
        {
            return left.Equals(right, StringComparison.OrdinalIgnoreCase);
        }
    }
    [Extension]
    public static class AssemblyExtensions
    {
        /// <summary>
        /// Loads the specified manifest resource, scoped by the namespace 
        /// of the specified type, from this assembly and returns
        /// it ready for reading as <see cref="TextReader"/>.
        /// </summary>

        public static TextReader GetManifestResourceReader(this Assembly assembly, Type type, string name)
        {
            return GetManifestResourceReader(assembly, type, name, null);
        }

        /// <summary>
        /// Loads the specified manifest resource, scoped by the namespace 
        /// of the specified type, from this assembly and returns
        /// it ready for reading as <see cref="TextReader"/>. A parameter
        /// specifies the text encoding to be used for reading.
        /// </summary>

        public static TextReader GetManifestResourceReader(this Assembly assembly, Type type, string name, Encoding encoding)
        {
            if (assembly == null) throw new ArgumentNullException("assembly");
            var stream = assembly.GetManifestResourceStream(type, name);
            if (stream == null)
                return null;
            return encoding == null
                 ? new StreamReader(stream, true)
                 : new StreamReader(stream, encoding);
        }

        /// <summary>
        /// Loads the specified manifest resource and returns it as a string, 
        /// scoped by the namespace  of the specified type, from this assembly. 
        /// </summary>

        public static string GetManifestResourceString(this Assembly assembly, Type type, string name)
        {
            return GetManifestResourceString(assembly, type, name, null);
        }

        /// <summary>
        /// Loads the specified manifest resource and returns it as a string, 
        /// scoped by the namespace  of the specified type, from this assembly. 
        /// A parameter specifies the text encoding to be decode the resource
        /// bytes into text.
        /// </summary>

        public static string GetManifestResourceString(this Assembly assembly, Type type, string name, Encoding encoding)
        {
            if (assembly == null) throw new ArgumentNullException("assembly");
            using (var reader = assembly.GetManifestResourceReader(type, name))
                return reader != null ? reader.ReadToEnd() : null;
        }
    }
    [Extension]
    public static class ArrayExtensions
    {
        /// <summary>
        /// Protects an array of bytes containing sensitive by encrypting 
        /// the data based on the user profile or machine.
        /// </summary>
        public static byte[] Protect(this byte[] bytes, DataProtectionScope scope)
        {
            return Protect(bytes, scope, null);
        }

        /// <summary>
        /// Protects an array of bytes containing sensitive by encrypting 
        /// the data based on the user profile or machine. An additional
        /// parameter specifies the entropy to randomize the encrpytion.
        /// </summary>
        public static byte[] Protect(this byte[] bytes, DataProtectionScope scope, byte[] entropy)
        {
            if (bytes == null) throw new ArgumentNullException("bytes");
            return ProtectedData.Protect(bytes, entropy, scope);

        }

        /// <summary>
        /// Unprotects an array of bytes containing sensitive by decrypting 
        /// the data based on the user profile or machine.
        /// </summary>

        public static byte[] Unprotect(this byte[] bytes, DataProtectionScope scope)
        {
            return Unprotect(bytes, scope, null);
        }
        /// <summary>
        /// Unprotects an array of bytes containing sensitive by decrypting 
        /// the data based on the user profile or machine. An additional
        /// parameter specifies the entropy to randomize the encrpytion.
        /// </summary>

        public static byte[] Unprotect(this byte[] bytes, DataProtectionScope scope, byte[] entropy)
        {
            if (bytes == null) throw new ArgumentNullException("bytes");
            return ProtectedData.Unprotect(bytes, entropy, scope);
        }

        public static bool IsEmpty(this Array array)
        {
            return (array == null) || (array.Length == 0);
        }
        public static int GetSafeCount(this Array array)
        {
            // allways check null value!
            return (array == null) ? 0 : array.Length;
        }
        public static int GetSafeCount<T>(this ICollection<T> col)
        {
            // allways check null value!
            return (col == null) ? 0 : col.Count;
        }
        public static T GetByPercent<T>(this T[] array, double percent)
        {
            return array[((array.Length - 1) * percent).Round()];
        }
        public static List<T> RemoveDuplicates<T>(this List<T> input)
        {
            Dictionary<T, int> tmp = new Dictionary<T, int>();

            foreach (T s in input)
                tmp[s] = 1;

            return input = new List<T>(tmp.Keys);
        }
        public static Array Merge<T>(this Array arrayOne, Array arrayTwo)
        {
            Array outArray = arrayOne;
            arrayTwo.CopyTo(outArray, outArray.Length);
            return outArray;
        }
        public static IList<T> Merge<T>(this IList<T> arrayOne, IList<T> arrayTwo)
        {
            T[] outArray = new T[arrayOne.Count + arrayTwo.Count];

            arrayOne.CopyTo(outArray , 0);
            arrayTwo.CopyTo(outArray, arrayOne.Count);
            return outArray;
        }
        
        #region Signum_Framework [OrginalAuthor(Url = "http://signum.codeplex.com")]

        public static T[,] Initialize<T>(this T[,] array, Func<int, int, T> valueXY)
        {
            for (int j = 0; j < array.GetLength(1); j++)
                for (int i = 0; i < array.GetLength(0); i++)
                    array[i, j] = valueXY(i, j);

            return array;
        }

        public static T[, ,] Initialize<T>(this T[, ,] array, Func<int, int, int, T> valueXYZ)
        {
            for (int k = 0; k < array.GetLength(2); k++)
                for (int j = 0; j < array.GetLength(1); j++)
                    for (int i = 0; i < array.GetLength(0); i++)
                        array[i, j, k] = valueXYZ(i, j, k);

            return array;
        }

        public static S[] ToArray<T, S>(this IEnumerable<T> collection, Func<T, S> value, Func<T, int> xPos)
        {
            if (collection.Empty())
                return new S[0];

            S[] result = new S[collection.Select(xPos).Max() + 1];
            foreach (var item in collection)
                result[xPos(item)] = value(item);
            return result;
        }

        public static S[,] ToArray<T, S>(this IEnumerable<T> collection, Func<T, S> value, Func<T, int> xPos, Func<T, int> yPos)
        {
            S[,] result = collection.Empty() ? new S[0, 0] : new S[collection.Max(xPos) + 1, collection.Max(yPos) + 1];
            foreach (var item in collection)
                result[xPos(item), yPos(item)] = value(item);
            return result;
        }

        public static S[, ,] ToArray<T, S>(this IEnumerable<T> collection, Func<T, S> value, Func<T, int> xPos, Func<T, int> yPos, Func<T, int> zPos)
        {
            S[, ,] result = collection.Empty() ? new S[0, 0, 0] : new S[collection.Max(xPos) + 1, collection.Max(yPos) + 1, collection.Max(zPos) + 1];
            foreach (var item in collection)
                result[xPos(item), yPos(item), zPos(item)] = value(item);
            return result;
        }

        public static IEnumerable<T> Row<T>(this T[,] data, int row)
        {
            for (int i = 0; i < data.GetLength(0); i++)
                yield return data[i, row];
        }

        public static IEnumerable<T> Column<T>(this T[,] data, int column)
        {
            for (int j = 0; j < data.GetLength(1); j++)
                yield return data[column, j];
        }

        public static T[,] AddRow<T>(this T[,] values, int pos, T[] newValues)
        {
            return AddRow(values, pos, i => newValues[i]);
        }

        public static T[,] AddRow<T>(this T[,] values, int pos, Func<int, T> newValue)
        {
            int width = values.GetLength(0);
            int height = values.GetLength(1);
            T[,] result = new T[width, height + 1];
            for (int j = 0; j < pos; j++)
                for (int i = 0; i < width; i++)
                    result[i, j] = values[i, j];

            for (int i = 0; i < width; i++)
                result[i, pos] = newValue(i);

            for (int j = pos; j < height; j++)
                for (int i = 0; i < width; i++)
                    result[i, j + 1] = values[i, j];
            return result;
        }

        public static T[,] AddColumn<T>(this T[,] values, int pos, T[] newValues)
        {
            return AddColumn(values, pos, i => newValues[i]);
        }

        public static T[,] AddColumn<T>(this T[,] values, int pos, Func<int, T> newValue)
        {
            int width = values.GetLength(0);
            int height = values.GetLength(1);
            T[,] result = new T[width + 1, height];
            for (int j = 0; j < height; j++)
            {
                for (int i = 0; i < pos; i++)
                    result[i, j] = values[i, j];
                result[pos, j] = newValue(j);
                for (int i = pos; i < width; i++)
                    result[i + 1, j] = values[i, j];
            }
            return result;
        }

        public static S[,] SelectArray<T, S>(this T[,] values, Func<T, S> selector)
        {
            return new S[values.GetLength(0), values.GetLength(1)].Initialize((i, j) => selector(values[i, j]));
        }

        public static S[,] SelectArray<T, S>(this T[,] values, Func<int, int, T, S> selector)
        {
            return new S[values.GetLength(0), values.GetLength(1)].Initialize((i, j) => selector(i, j, values[i, j]));
        }

        public static S[, ,] SelectArray<T, S>(this T[, ,] values, Func<T, S> selector)
        {
            return new S[values.GetLength(0), values.GetLength(1), values.GetLength(2)].Initialize((i, j, k) => selector(values[i, j, k]));
        }

        public static S[, ,] SelectArray<T, S>(this T[, ,] values, Func<int, int, int, T, S> selector)
        {
            return new S[values.GetLength(0), values.GetLength(1), values.GetLength(2)].Initialize((i, j, k) => selector(i, j, k, values[i, j, k]));
        }

        #endregion
    }
    [Extension]
    public static class FormExtensions
    {
        [Serializable, Description("Used for saving form size+pos")]
        public class FormPosInfo
        {
            [DefaultValue(0)]
            private int _x;
            [DefaultValue(0)]
            private int _y;
            [DefaultValue(0)]
            private int _width;
            [DefaultValue(0)]
            private int _height;
            public int X { get { return _x; } set { _x = value; } }
            public int Y { get { return _y; } set { _y = value; } }
            public int Width { get { return _width; } set { _width = value; } }
            public int Height { get { return _height; } set { _height = value; } }
            public FormPosInfo(int x, int y, int width, int height)
            {
                this._x = x;
                this._y = y;
                _width = width;
                _height = height;
            }
            public FormPosInfo() { }
        }
        public static FormPosInfo LoadFormState(this Form f)
        {
            //IntPtr formHandle = f.Handle;
            //Control c = null;
            //Control main = null;
            //while (c.Parent != null)
            //{
            //    c = c.Parent;
            //    main = c;
            //}
            

            //foreach (Process proc in Process.GetProcesses())
            //{
            //    if (MainFormHandle == proc.Handle)//(proc.Handle.Is(MainFormHandle))
            //    {
            //        MainProc = proc;
            //        break;
            //    }
            //}
            //var r = Hack.Helpers.ProcessHelpers.FindProcess(f.Text);
            //var rr = "";
            //AnytimeMessageBox.Say(Hack.Helpers.ProcessHelpers.FindProcess(f.Handle).StartInfo.FileName, AnytimeMessageBox.TitleTypes.Information);
            //if (MainProc != null)
            //{

            var savePath = Application.StartupPath + "\\" + f.Name + "-Settins.dat";

     
            FormPosInfo posinfo = null;
            File.Exists(savePath).IfTrue(() =>
            {
                //try
                //{
                posinfo = (FormPosInfo)new byte[] { 0x00 }.LoadFromFile(savePath).Decompress().DeSerialize();//object bytes = Hack.Extensions.ByteExtensions.LoadFromFile(null, savePath).DeSerialize();
                //}
                //catch { }
                //try
                //{
                //    FileStream fs = File.OpenRead(savePath);
                //    byte[] bytes = new byte[fs.Length];
                //    int numBytesToRead = (int)fs.Length;
                //    int numBytesRead = 0;
                //    while (numBytesToRead > 0)
                //    {
                //        int n = fs.Read(bytes, numBytesRead, numBytesToRead);
                //        if (n == 0)
                //            break;
                //        numBytesRead += n;
                //        numBytesToRead -= n;
                //    }
                //    numBytesToRead = bytes.Length;
                //    posinfo = (FormPosInfo)bytes.DeSerialize();

                //}
                //catch { }
            });
            if (posinfo != null)
            {
                f.Location = new Point(posinfo.X, posinfo.Y);

                f.Width = posinfo.Width;
                f.Height = posinfo.Height;

                return posinfo;
            }
            //}
            return new FormPosInfo { X = 0, Y = 0, Width = 0, Height = 0 };
        }
        public static FormPosInfo SaveFormState(this Form f)
        {
            //IntPtr formHandle = f.Handle;
            //Control c = null;
            //Control main = null;
            //while (c.Parent != null)
            //{        
            //    c = c.Parent;
            //    main = c;
            //}
            //IntPtr MainFormHandle = main.Handle;
            //Process MainProc = null;
            //foreach (Process proc in Process.GetProcesses())
            //{
            //    if (proc.Handle.Is(MainFormHandle))
            //    {
            //        MainProc = proc;
            //        break;
            //    }
            //}
            //if (MainProc != null)
            //{

            //    string savePath = Path.Combine(Path.GetTempPath(), MainProc.StartInfo.FileName);
            string savePath = Application.StartupPath + "\\" + f.Name + "-Settins.dat";
            FormPosInfo posinfo = new FormPosInfo { X = f.Location.X, Y = f.Location.Y, Width = f.Width, Height = f.Height };
            //File.Exists(savePath).IfTrue(() =>
            //{
            //    Hack.Extensions.ByteExtensions.SaveToFile(posinfo.Serialize(), savePath);
            //});
            //if (File.Exists(savePath))
            //{

            //}
            Hack.Extensions.ByteExtensions.SaveToFile(posinfo.Serialize().Compress(), savePath);
            return posinfo;
            //AnytimeMessageBox.Say(savePath, AnytimeMessageBox.TitleTypes.Information);
            //FileStream fs = null;
            //File.Exists(savePath).IfFalse(() => { File.Create(savePath); fs = File.OpenWrite(savePath); });
            //FormPosInfo posinfo = new FormPosInfo { X = f.Location.X, Y = f.Location.Y, Width = f.Width, Height = f.Height };
            //if (fs == null)
            //{
            //    //Exists
            //    fs = File.OpenWrite(savePath);

            //}
            //byte[] toWrite = posinfo.Serialize();
            //fs.Write(toWrite, 0, toWrite.Length);

            // MainProc.StartInfo.FileName.
            //return posinfo;
            //}
            //return new FormPosInfo { X = 0, Y = 0, Width = 0, Height = 0 };
        }
        public static IEnumerable<Control> GetControls(this Control form)
        {
            foreach (Control childControl in form.Controls)
            {   // Recurse child controls.
                foreach (Control grandChild in GetControls(childControl))
                {
                    yield return grandChild;
                }
                yield return childControl;
            }
        }
    }
    /// <summary>
    ///     Mannex - Extension methods for .NET
    /// Copyright (c) 2009 Atif Aziz. All rights reserved.
    ///
    ///  Author(s):
    ///
    ///      Atif Aziz, http://www.raboof.com
    ///
    /// Licensed under the Apache License, Version 2.0 (the ""License"");
    /// you may not use this file except in compliance with the License.
    /// You may obtain a copy of the License at
    ///
    ///    http://www.apache.org/licenses/LICENSE-2.0
    ///
    /// Unless required by applicable law or agreed to in writing, software
    /// distributed under the License is distributed on an ""AS IS"" BASIS,
    /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    /// See the License for the specific language governing permissions and
    /// limitations under the License.
    /// </summary>
    [Extension, OrginalAuthor(Author = "Atif Aziz", Url = "http://www.raboof.com", Description = "Mannex - Extension methods for .NET")]
    public static class ProcessExtensions
    {
        public static Exception TryKill(this Process process)
        {
            if (process == null) throw new ArgumentNullException("process");

            try
            {
                process.Kill();
                return null;
            }
            catch (InvalidOperationException e)
            {
                // Occurs when:
                // - process has already exited. 
                // - no process is associated with this Process object.
                return e;
            }
            catch (Win32Exception e)
            {
                // Occurs when:
                // - associated process could not be terminated. 
                // - process is terminating.
                // - associated process is a Win16 executable.
                return e;
            }
        }

        public static bool WaitForExit(this Process process, TimeSpan? timeout)
        {
            if (process == null) throw new ArgumentNullException("process");
            return process.WaitForExit(timeout.ToTimeout());
        }
        public static Action<TimeSpan?> BeginReadLine(this Process process, TextWriter output)
        {
            return BeginReadLine(process, output, null);
        }
        public static Action<TimeSpan?> BeginReadLine(this Process process, TextWriter output, TextWriter error)
        {
            if (process == null) throw new ArgumentNullException("process");

            return BeginReadLineImpl(process,
                (output ?? TextWriter.Null).WriteLine,
                (error ?? TextWriter.Null).WriteLine);
        }
        public static Action<TimeSpan?> BeginReadLine(this Process process, Action<string> output)
        {
            return BeginReadLine(process, output, null);
        }
        public static Action<TimeSpan?> BeginReadLine(this Process process, Action<string> output, Action<string> error)
        {
            if (process == null) throw new ArgumentNullException("process");

            return BeginReadLineImpl(process,
                output ?? (TextWriter.Null.WriteLine),
                error ?? (TextWriter.Null.WriteLine));
        }

        private static Action<TimeSpan?> BeginReadLineImpl(Process process, Action<string> output, Action<string> error)
        {
            var outeof = new ManualResetEvent(false);
            process.OutputDataReceived += OnDataReceived(output, () => outeof.Set());
            process.BeginOutputReadLine();

            var erreof = new ManualResetEvent(false);
            process.ErrorDataReceived += OnDataReceived(error, () => erreof.Set());
            process.BeginErrorReadLine();

            return timeout => WaitHandle.WaitAll(new[] { outeof, erreof }, timeout.ToTimeout());
        }

        private static DataReceivedEventHandler OnDataReceived(
            Action<string> line, Action eof)
        {
            return (sender, e) =>
            {
                if (e.Data != null)
                    line(e.Data);
                else
                    eof();
            };
        }
    }

}